﻿#region License
/*
    Copyright (c) 2010, Paweł Hofman (CodeTitans)
    All Rights Reserved.

    Licensed under the Apache License version 2.0.
    For more information please visit:

    http://codetitans.codeplex.com/license
        or
    http://www.apache.org/licenses/


    For latest source code, documentation, samples
    and more information please visit:

    http://codetitans.codeplex.com/
*/
#endregion

using System;
using System.Text;
using System.Collections.Generic;
using CodeTitans.JSon;
using CodeTitans.NUnitTests;
using System.IO;
using System.Collections;
using System.Diagnostics;
#if NUNIT
using NUnit.Framework;
using TestClassAttribute=NUnit.Framework.TestFixtureAttribute;
using TestMethodAttribute=NUnit.Framework.TestAttribute;
using TestInitializeAttribute=NUnit.Framework.SetUpAttribute;
using TestCleanupAttribute=NUnit.Framework.TearDownAttribute;
#else
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Reflection;
#endif

namespace CodeTitans.UnitTests.JSon
{
    [TestClass]
    public class JSonReaderTests
    {
        private JSonReader reader;

        [TestInitialize]
        public void TestInit()
        {
            reader = new JSonReader();
        }

        [TestMethod]
        public void ParseToJSon()
        {
            string jsonText = "[{\"minimumVersion\":\"1.0\",\"type\": null,\"channel\":\"\\/meta\\/handshake\",\"supportedConnectionTypes\":[\"request-response\"],\"successful\":true}]";

            var result = reader.ReadAsJSonObject(jsonText);
            Assert.AreEqual(result[0]["channel"].StringValue, "/meta/handshake", "Invalid channel");
            Assert.IsTrue(result[0]["type"].IsNull, "Type should be null");
            Assert.AreEqual(result[0]["supportedConnectionTypes"].Count, 1, "Supported types are an array with 1 element");
            Assert.AreEqual(result[0]["minimumVersion"].DoubleValue, 1.0, "Current version is 1.0");
            Assert.IsTrue(result[0]["successful"].IsTrue, "Operation finished with success!");
        }

        [TestMethod]
        public void ParseEmptyJSonString()
        {
            var result = reader.Read("  ");

            Assert.IsNull(result, "Result should be null");
        }

        [TestMethod]
        public void ParseEmptyArray()
        {
            var result = reader.Read("  [    ]");

            AssertExt.IsInstanceOfType(result, typeof(Array), "Result should be an array");
        }

        [TestMethod]
        [ExpectedException(typeof(JSonReaderException))]
        public void ParseWrongOpenArray()
        {
            var result = reader.Read("  [   ");
        }

        [TestMethod]
        [ExpectedException(typeof(JSonReaderException))]
        public void ParseWrongCloseArray()
        {
            var result = reader.Read("  \t\t\n ]   ");
        }

        [TestMethod]
        [ExpectedException(typeof(JSonReaderException))]
        public void ParseUnfinishedString()
        {
            var result = reader.Read("   \"start");
        }

        [TestMethod]
        [ExpectedException(typeof(JSonReaderException))]
        public void ParseWrongNumberOfMainElements()
        {
            var result = reader.Read("  [ ], [], { }  ");
        }

        [TestMethod]
        [ExpectedException(typeof(JSonReaderException))]
        public void ParseArrayWithoutComma()
        {
            var result = reader.Read(" [ \"a\", \"b\" \"c\" ]");
        }

        [TestMethod]
        [ExpectedException(typeof(JSonReaderException))]
        public void ParseTopLevelElementsWithoutComma()
        {
            var result = reader.Read("  [ ] [] { }  ");
        }

        [TestMethod]
        public void ParseKeyword()
        {
            var result = reader.Read("  null  ");

            AssertExt.IsInstanceOfType(result, typeof(DBNull), "Item should be null");
        }

        [TestMethod]
        public void ParseNumber()
        {
            var result = reader.Read("  10.1e-2  ");

            AssertExt.IsInstanceOfType(result, typeof(double), "Item should be a number");
            Assert.AreEqual(result, 10.1e-2);
        }

        [TestMethod]
        [ExpectedException(typeof(JSonReaderException))]
        public void ParseInvalidNumber()
        {
            var result = reader.Read("  10,1e-2  ");
        }

        [TestMethod]
        public void ParseKeywords()
        {
            var result = reader.ReadAsJSonObject("   null");

            Assert.AreEqual(result.StringValue, null, "Expected a null string!");

            result = reader.ReadAsJSonObject("  true  ");
            Assert.AreEqual("true", result.StringValue, "Expected 'true' string returned");
            Assert.AreEqual(true, result.BooleanValue, "Expected boolean true value");
            Assert.AreEqual(1, result.DoubleValue, "Expected non zero value");
        }

        [TestMethod]
        public void ParseString()
        {
            var result = reader.Read("  \"Paweł\\r\\nJSON\\r\\nText\ttab\tspaced.\"  ");

            AssertExt.IsInstanceOfType(result, typeof(string), "Item should be a string");
        }

        [TestMethod]
        [ExpectedException(typeof(JSonReaderException))]
        public void ParseInvalidNotFinishedString()
        {
            var result = reader.Read("  \"Not finished text  ");
        }

        [TestMethod]
        [ExpectedException(typeof(JSonReaderException))]
        public void ParseInvalidStringWithNewLine()
        {
            var result = reader.Read("  \"Test string with new line\r\n\"");
        }

        [TestMethod]
        public void ParseToLongUnicodeCharacterDefinitionInString()
        {
            var result = reader.Read("  \"Text-\\u12345\"");

            Assert.IsNotNull(result, "Result can't be null");
            Assert.AreEqual(result, "Text-\u12345", "Invalid text read");
        }

        [TestMethod]
        [ExpectedException(typeof(JSonReaderException))]
        public void ParseInvalidUnicodeCharacters()
        {
            var result = reader.Read("\"\\u12\\u123\"");
        }

        [TestMethod]
        public void ParseSeveralUnicodeCharacters()
        {
            var result = reader.Read("\"\\u308f\\u3084\\u307e\\u3061\\u306a\\u306a\"");

            Assert.IsNotNull(result, "Value should be some string");
            AssertExt.IsInstanceOfType(result, typeof(string), "Value shoule be string class");
            Assert.AreEqual(result, "\u308f\u3084\u307e\u3061\u306a\u306a");
        }

        [TestMethod]
        public void ParseValidUnicodeCharacterInString()
        {
            var result = reader.Read("  \"\\u0020\"");

            AssertExt.IsInstanceOfType(result, typeof(string), "Item should be string");
            Assert.AreEqual(result, " ", "Single space was provided as input!");
        }

        [TestMethod]
        public void ParseStringSpecialCharacter()
        {
            var result = reader.Read("  \"\\\"\"");

            AssertExt.IsInstanceOfType(result, typeof(string), "Item should be string");
            Assert.AreEqual(result, "\"", "Expected special character!");
        }

        [TestMethod]
        public void ParseArray()
        {
            var result = reader.Read(" [ \"a\", \"b\", \"c\" ]");

            AssertExt.IsInstanceOfType(result, typeof(Array), "Item should be an array");
            Assert.AreEqual(((Array)result).Length, 3, "Invalid number of items");
            Assert.AreEqual(((object[])result)[0], "a", "Invalid first element");
        }

        [TestMethod]
        [ExpectedException(typeof(JSonReaderException))]
        public void ParseEmptyArrayWithComma()
        {
            var result = reader.Read(" [,] ");
        }

        [TestMethod]
        public void ParseEmptyObject()
        {
            var result = reader.Read("  { } ");

            AssertExt.IsInstanceOfType(result, typeof(IDictionary), "Item should be a dictionary");
        }

        [TestMethod]
        [ExpectedException(typeof(JSonReaderException))]
        public void ParseEmptyObjectWithComma()
        {
            var result = reader.Read(" {,} ");
        }

        [TestMethod]
        public void ParseSimpleData1()
        {
            var result = reader.Read("  { \"A\":1, \"B\": 2   , \"c\": 12.1 } ");

            AssertExt.IsInstanceOfType(result, typeof(IDictionary), "Item should be a dictionary");
        }

        [TestMethod]
        [DeploymentItem(@"Resources\advanced.json")]
        public void ParseAdvancedStructure()
        {
            var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\";
            var jsonText = File.Exists(path + "advanced.json") ? File.ReadAllText(path + "advanced.json"): File.ReadAllText(path + "Resources\\advanced.json");
            var watchReader = Stopwatch.StartNew();
            var result = reader.ReadAsJSonObject(jsonText);
            watchReader.Stop();

            var watchWriter = Stopwatch.StartNew();
            var writer = new JSonWriter(true);
            writer.Write(result);
            watchWriter.Stop();
            Console.WriteLine("Parsing taken: {0}", watchReader.Elapsed);
            Console.WriteLine("Serializing taken: {0}", watchWriter.Elapsed);
            Console.WriteLine("{0}", writer.ToString());

            Assert.IsNotNull(result);
        }

        [TestMethod]
        [DeploymentItem(@"Resources\guid.json")]
        public void ParseAdvancedGuidArray()
        {
            var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\";
            var jsonText = File.Exists(path + "guid.json") ? File.ReadAllText(path + "guid.json"): File.ReadAllText(path + "Resources\\guid.json");
            var watchReader = Stopwatch.StartNew();
            var result = reader.ReadAsJSonObject(jsonText);
            watchReader.Stop();

            Assert.IsNotNull(result);
            AssertExt.IsInstanceOfType(result[0].GuidValue, typeof(Guid), "Read element should be Guid");
            Console.WriteLine("Parsing taken: {0}", watchReader.Elapsed);
        }

        [TestMethod]
        public void ParseBayeuxConnectResponse()
        {
            var result = reader.ReadAsJSonObject("[{\"channel\":\"/meta/connect\",\"advice\":{\"reconnect\":\"retry\",\"interval\":0,\"timeout\":20000},\"successful\":true,\"id\":\"1\"}]");

            Assert.IsNotNull(result, "Invalid parsed object!");

            int timeout = result[0]["advice"]["timeout"].Int32Value;
            Assert.AreEqual(20000, timeout, "Invalid timeout value!");
        }
    }
}
